/** @file   temp_sensor.c
    @author M. P. Hayes, UCECE
    @date   6 Aug 2007
    @brief  Driver for ds18b20 temperature sensor.
    @note   Currently this only supports a single temperature sensor.
*/
#include "config.h"
#include "u1wire.h"
#include "ds18b20.h"
#include "temp_sensor.h"

static u1wire_obj_t dev;
static bool dev_ok;


/** Scan for temperature sensor on the one wire bus.
    @return number of temperature sensors found or negative for error  */
int8_t
temp_sensor_init (void)
{
    int8_t ret;

    /* Check that there is at least one device on the bus.  */
    ret = u1wire_init (&dev);

    if (!ret)
	return -U1WIRE_ERR_DISCONNECT;

    if (ret != 1)
	return ret;

    if (! ds18b20_init (&dev))
	return -U1WIRE_ERR_WRONG_DEVICE;

    dev_ok = 1;
    return 1;
}


/** Start a temperature conversion on selected channel
    @param channel temperature sensor channel to start
    @return 1 if OK, 0 if no device responding, negative if error  */
int8_t
temp_sensor_start (uint8_t channel)
{
    if (channel >= 1)
	return 0;

    return ds18b20_temp_conversion_start (&dev);
}


/** Read temperature value from selected channel. 
    @param channel temperature sensor channel to read
    @param ptemp pointer to temperature to return
    @return 1 if OK, 0 if no device responding, negative if error
    @note This blocks until the temperature conversion has completed.
    @note \c temp_sensor_start () must have been previously called.  */
int8_t
temp_sensor_read (uint8_t channel, temp_sensor_temp_t *ptemp)
{
    ds18b20_temp_t temp;
    int8_t ret;

    if (channel >= 1)
	return 0;

    /* Wait until a temperature conversion has completed.  */
    while (!ds18b20_temp_ready_p (&dev))
	continue;

    /* Read the temperature and convert the fixed point temperature
       value to an integer.  */
    ret = ds18b20_temp_read (&dev, &temp);
    *ptemp = DS18B20_TEMP_INT (temp);
    
    return ret;
}


/** Read temperature value from selected channel.
    @param channel temperature sensor channel to read
    @param ptemp pointer to temperature to return
    @return 1 if OK, 0 if no device responding, negative if error
    @note This blocks until the temperature conversion has completed.
    @note This starts a new conversion and will keep looking for
    a temperature sensor.  */
int8_t
temp_sensor_get (uint8_t channel, temp_sensor_temp_t *ptemp)
{
    int8_t ret;

    if (channel >= 1)
	return 0;

    if (!dev_ok)
    {
	/* Keep trying to find temperature sensor in case it is
	   not plugged in.  */
	ret = temp_sensor_init ();
	if (ret != 1)
	    return ret;
	
	temp_sensor_start (channel);
	dev_ok = 1;
	return 0;
    }
    
    /* Grab temperature reading and restart another conversion.  */
    ret = temp_sensor_read (channel, ptemp);
    if (ret <= 0)
    {
	*ptemp = 0;
	dev_ok = 0;
	return ret;
    }

    /* Start next temperature conversion.  */
    temp_sensor_start (channel);	    
    return 1;
}
